.so ../bk-macros
.TH "bk triggers" "\*[BKVER]" %E% "\*(BC" "\*(UM"
.SH NAME
bk triggers \- using \*[BK] event triggers
.SH DESCRIPTION
.LP
\*[BK] supports a variety of trigger types.   Triggers are simple programs,
usually shell scripts, which may be called before and/or after certain
events, such as a commit, pull, push, resolve, etc.
Triggers can be used for event notification 
and/or to implement control over the events themselves.
.LP
When a trigger is called, it is called with the current working
directory set to the root of the  repository.
Note that in the case of incoming data, the root of the
repository is defined as the
.B RESYNC
directory.
.LP
If an incoming changeset adds or updates a trigger, the incoming trigger is not
the trigger fired, with the exception of the post-incoming trigger.
The trigger already present in the repository, if any, is the trigger
used.  This arrangement is for security reasons; incoming changes could
be malicious or ill advised and a prudent repository manager may have
developed triggers to look for problems.  If the incoming data contained
a new or modified trigger, and that trigger was run, triggers could not
be used to implement security or other policies at the repository boundary.
.SS
TRIGGER NAMES
.LP
When an event occurs, if there exists a 
file
.BI BitKeeper/triggers/ event_class
in the repository root that corresponds to the event, 
\*[BK] will execute that trigger.  For example, if there is
a push from repository B going into repository A 
and repository A has a file
.BR BitKeeper/triggers/pre-incoming ,
the pre-incoming script will be run before the push event 
applies to repository A.
.LP
All triggers for a particular class must be named with
the class name and prefix, for example
.BR pre-incoming-ok .
More than one trigger per event class is allowed; each trigger program
has the class name and prefix and a suffix of your choosing.  The trigger
programs are sorted and run alphabetically (C locale sort order).  
.LP
If there are multiple pre- triggers (see below), the first trigger to
exit with a non-zero status will halt the trigger processing.
If there are pre- triggers that must always be run, they must be named
such that their name will sort earlier than any other trigger of that
class.
.LP
In order
to avoid name space conflicts, the typical approach is to use the reason
for the trigger as the suffix, i.e.,
.DS
post-incoming.mail
post-incoming.regression-test
.DE
Files ending in ~ are ignored to avoid editor backup files.
.\" Doc writers note: replicate this in bk-config-etc.1
.SS TRIGGER PATHS
.LP
By default, triggers are stored in the repository under the
.B BitKeeper/triggers/
directory and this is the only directory searched when looking for
triggers.
More than one triggers directory may be used by setting the
.B triggers
variable.
The format is one or more paths separated by a vertical bar,
each path has "BitKeeper/triggers" appended to it and the resulting
path is scanned for triggers.
For example, if you wanted to run triggers from
.B /etc/BitKeeper/triggers
and from the repositories'
.BR BitKeeper/triggers ,
set the variable as follows in your configuration:
.DS
triggers:	/etc|.
.DE
The directories are processed in the order found in the variable.
.LP
There are several special values which are interpreted:
.TP 
.B .
It means `bk -R pwd`/BitKeeper/triggers is scanned
for triggers.
.TP
.B $BK_DOTBK
If present, `bk dotbk`/BitKeeper/triggers is scanned for triggers.
.TP
.B $BK_BIN
If present, `bk bin`/BitKeeper/triggers is scanned for triggers.
.TP
.B $NONE
If present, with no other values, then no triggers are processed.
.TP
.B $PRODUCT
If present, `bk -P pwd`/BitKeeper/triggers is scanned for triggers.
This only applies when in a component repository of a nested collection.
It is a way to run product level triggers in each component.
.TP
.B $SOMETHING_ELSE
All other paths starting with "$" are ignored, that character is
reserved.
.LP
If the the variable is not defined, the default is:
.DS
triggers:	$PRODUCT|.
.DE
.SS
TRIGGER CLASSES
.LP
There are multiple event classes which may activate triggers: incoming
events, outgoing events, deltas, commits, tags, undo, and collapses.
The incoming event is broken into
multiple events: incoming, resolve, and apply.
.LP
Most events may have triggers which run before and/or after the event.
The difference between
pre- and post- event triggers is that pre-triggers may cause events
to fail, but post-triggers are strictly informational.   
The exit status from post-triggers are ignored.
.LP
Not all triggers have both pre- and post- versions, the set of
supported triggers are as follows:
.de EV
.	if t .TP 
.	if n .TP 2
.	B \\$1
.	li
..
.EV pre-apply
called after the data has been merged in the
.B RESYNC
directory but
before it is applied to the tree.  Last chance to say no, allows examination
of the merged changes.
.li
called in the
.B RESYNC
directory, not the enclosing repository.
.li
exit 0 allows the pull/push.
.li
exit 1 fails the entire pull/push.
.li
exit 2 fails the pull/push but leaves the patch in
.BR PENDING .
.li
exit 3 fails the pull/push but leaves the patch in
.B PENDING
and the
.B RESYNC
tree in
.BR PENDING/RESYNC-\fIdate\fP .
.hy
.EV pre-collapse
called before a changeset is collapsed (see
.BR "bk collapse" ).
Typically used as part of a process to record the renaming of changesets in
bug tracking systems.
.li
exit 0 allows the collapse.
.li
non-zero exit values will fail the collapse.
.EV pre-commit
called before a changeset is committed.
.li
exit 0 allows the commit. 
.li
exit 1 fails the commit. If the commit was initiated from citool, 
citool will exit.
See example below.
.li
exit 2 also fails the commit.  However, if it was initiated from citool,
citool will not exit.  This allows the user to try the commit again.
See example below.
.EV post-commit
called after a changeset is committed or attempted to be committed.
.li
typically used for notification.
.EV pre-delta
called before a delta is created. Can be use for triggers that check code style
.li
exit 0 allows the delta.
.li
exit 2 indicates that the trigger called delta itself, upon which the
calling delta command treats it as a successful delta.
.li
Other than 2, all other non-zero exit values will fail the delta.
.EV pre-incoming
called before an incoming push/pull is started.
.li
non-zero exit status fails the incoming event.
.li
typically used for locking down a repository.
.li
may be used to fail the event based on remote user, host, directory,
and/or \*[BK] version.
.EV post-incoming
called after the data has been applied to the tree.
.li
typically used for notification.
.EV pre-outgoing
called before an outgoing pull/push/clone event.
.li
non-zero exit status fails the outgoing event.
.li
typically used for locking down a repository.
.EV post-outgoing
called after the outgoing event.
.li
typically used for notification.
.EV pre-resolve
called after the data has been union-ed in the
.B RESYNC
directory.
.li
called in the
.B RESYNC
directory, not the enclosing repository.
.li
exit 0 allows the pull/push.
.li
exit 1 fails the entire pull/push.
.li
exit 2 fails the entire pull/push but leaves the patch in
.BR PENDING .
.li
typically used to examine changes before taking them.
.EV pre-tag
called before a tag event, such as a
.B bk commit
with a specified tag or a
.BR "bk tag" .
.li
exit 0 allows the tag.
.li
exit 1 causes the tag operation to fail as well as the commit operation if
the tag was part of a commit.
.li
exit 2 causes the tag operation to fail but allows the commit operation to
proceed if the tag was part of a commit.
.EV pre-undo
called before a undo is run by user or by unpull and clone -r. 
.li
exit 0 allows the undo.
.li
exit 1 causes the undo operation to fail.
.EV post-undo
called after an undo is run successfully.
.SS
TRIGGER LOCKING
.LP
Triggers are called with a locked repository.
When a post trigger is called, the repository is read locked, even in 
the case that the event was an event which changed the repository.
A read lock will allow the trigger to do outgoing events, such as a push,
but will prevent incoming events.
Pre-incoming and pre-commit triggers will be called with a write locked
repository.
.SS
TRIGGER SECURITY ISSUES
.LP
Triggers are arbitrary programs (or scripts) which are run automatically
and without warning.  It is possible that a malicious person could add
a trigger which effect a security breach, damage files, etc.  If you are
operating in a non-trusted environment, you may disable all triggers by
setting the 
.V BK_NO_TRIGGERS
environment variable.  This is the
safest thing to do but then the trigger functionality is lost.
.LP
Alternatively, a \*(lqparanoid\*(rq trigger could be added which 
refused to accept a new trigger into a repository without being 
examined first.
Here's an example of such a trigger, which would typically be named
.BR BitKeeper/triggers/pre-apply.paranoid :
.DS
#!/bin/sh

# This is running in the RESYNC tree, we're looking
# for any new triggers and/or changes to triggers.
# Done after the resolve stage because they could
# be sneaky and create the file in an earlier
# changeset and then move it.

test `bk gfiles BitKeeper/triggers | wc -l\` -gt 0 || exit 0

if [ $BK_SIDE = server ]
then    echo Refusing to accept any changes to triggers on push,
        echo get the project admin to pull your changes.
        exit 1
fi

rm -f BitKeeper/tmp/t_reject
for i in \`bk gfiles BitKeeper/triggers\`
do      (
        echo Please review the following trigger for security risks.
        echo Do not accept it if you think it is a problem.
        echo
        echo ===== $i =====
        bk cat $i
        ) > BitKeeper/tmp/prompt$$
        bk prompt -fBitKeeper/tmp/prompt$$ \\
	    -t"Review trigger" -yAccept -nReject
        STATUS=$?
        rm -f BitKeeper/tmp/prompt$$
        test $STATUS = 0 || {
                touch BitKeeper/tmp/t_reject
                break
        }
done
test -f BitKeeper/tmp/t_reject && {
        rm -f BitKeeper/tmp/t_reject
        exit 3
}
rm -f BitKeeper/tmp/t_reject
exit 0
.DE
.SS
TRIGGER ENVIRONMENT VARIABLES
.LP
Information which might be useful to the trigger is passed in environment
variables.  There are variables for user, host, location, \*[BK] version,
repository level, amongst others.
There are two classes of variables, client side variables (\c
.V BK_* )
and server side variables (\c
.V BKD_* ).
The client side variables are associated with the user who
initiated the command.
The server side variables, if present, are associated with the \*(lqother\*(rq
repository.  For example, if a user on host \*(lqto\*(rq does a pull from host
\*(lqfrom\*(rq, then
.V BK_HOST =to
and
.V BKD_HOST =from.
In the list of variables which follow,
.V BKD_*
variables are not present unless the command has two end points,
such as  a pull, push, or clone.
The
.V BKD_*
variables are not defined for commit, resolve,
and apply events.
In all other cases, the variable is present in all triggers
unless otherwise stated.
.LP
.TP \fBBK_COMMENTFILE\fP
.V BK_CSETLIST 
If set, contains the name of a file which contains the list of changesets
being received.  Valid in
pre-apply,
post-incoming
pre-outgoing,
post-outgoing,
pre-resolve,
and
pre-undo
triggers.
.tp
.V BK_CSETS
If set, contains the list of changesets being sent.
Valid only in pre-outgoing and post-outgoing clone events.
.tp
.V BK_COMMENTFILE
Location of the comment file for the changeset. Useful when writing
triggers that need to parse the changeset comments. See example
below.
.tp
.V BK_EVENT
The event from the point of view of the trigger.  The full list of values
for this variable is:
apply,
collapse,
commit,
delta,
fix,
incoming clone,
incoming port,
incoming pull,
incoming push,
outgoing clone,
outgoing pull, 
outgoing push,
resolve,
tag,
and
undo.
.tp
.V BK_FILE
Valid only in the pre-delta trigger, and contains the filename of the file
about to be delta-ed, relative to the repository root.
.tp
.V BK_HOST
The hostname of the client side host.
.tp
.V BKD_HOST
The hostname of the server side host.
.tp
.V BK_LEVEL
The \*(lqlevel\*(rq of the client side repository.
.tp
.V BKD_LEVEL
The \*(lqlevel\*(rq of the server side repository.
.tp
.V BK_LOCALCSETS
The number of changesets (and/or tags) which are not
present in the remote repository but are present in the local repository.
Note that this variable does not have a
.V BKD_
version because it
is valid only on the outgoing end of a pull or a push.
The other variable is
.V BK_REMOTECSETS .
Both variables are valid in pre-outgoing and post-outgoing
triggers only.
.tp
.V BK_REPO_TYPE
The repository type.  One of product, component, or standalone.
.tp
.V BKD_REPO_TYPE
As above.
.tp
.V BK_PATCH
Valid only in the pre-resolve trigger,
and contains the full pathname of the file containing the patch being resolved.
.tp
.V BK_PENDING
Contains the name of a file which
contains the list of files with pending deltas.
Valid only in pre-commit.
.tp
.V BK_REMOTECSETS
The number of remote changesets (and/or tags) which are not
present in the local repository but are present in the remote
repository.
Goes with
.V BK_LOCALCSETS
and is valid only in pre-outgoing and
post-outgoing triggers.
.tp
.V BK_ROOT
The full path name to the root of the client side repository.
.tp
.V BKD_ROOT
The full path name to the root of the server side repository.
.tp
.V BK_SIDE
If the trigger is part of a two-sided operation (i.e., pull, push), then
this is set to \*(lqserver\*(rq if the trigger is running on the 
server repository.
Otherwise this is set to \*(lqclient.\*(rq
.tp
.V BK_STATUS
The status of the command, if known.  Values may include:
.RS 4
.TP LOCAL_WORK
NOTHING
There was nothing to pull or push.
.tp
FAILED
The command did not complete because of an error.
.tp
DRYRUN
The command did not complete because it was a \*(lqdry run,\*(rq i.e., a 
.Q bk pull \-n
to look to see if there is anything to pull.
.tp
CONFLICTS
The command did not complete because there were conflicts (parallel work).
.tp
LOCAL_WORK
The command did not complete because there is local work and an update only
operation was requested.
.tp
SIGNALED
The command did not complete because it received a signal.
.tp
OK
The command completed successfully.
.tp
UNKNOWN
Unknown status.
.RE
.tp
.V BK_TAG
If set, contains the value of the symbolic tag to be added to the repository.
Valid only in pre-tag trigger.
.tp
.V BK_TAG_REV
If set, contains the changeset revision on which the tag will be placed.
This will be set if and only if the revision is not the most recent revision.
Valid only in pre-tag trigger.
.tp
.V BK_TIME_T
The \*[UN] style time stamp of the client side \*[BK] binary.
.tp
.V BKD_TIME_T
The \*[UN] style time stamp of the server side \*[BK] binary.
.tp
.V BK_TRIGGER
The basename name of the trigger program.
.tp
.V BK_USER
The user name of the user who ran the command on the client.
.tp
.V BKD_USER
The user name of the user who ran the command on the server.
.tp
.V BK_UTC
The time stamp of the client side \*[BK] binary expressed as YYYYMMDDHHMMSS.
.tp
.V BKD_UTC
The time stamp of the server side \*[BK] binary expressed as YYYYMMDDHHMMSS.
.tp
.V BK_VERSION
The version of the client side \*[BK] binary as the symbolic name or the UTC.
.tp
.V BKD_VERSION
The version of the server side \*[BK] binary as the symbolic name or the UTC.
.SS
.SH "EXAMPLE 1"
.DS
#!/bin/sh

# Simple post-commit trigger for email notification of changes

# For nested collections, we don't want notification in components,
# the product recurses.
test `bk repotype` = "component" && exit 0

# Let bk changes do all the work for us.
bk changes -vvr+ |
    mail -s "commit in `bk gethost -r`:`bk pwd` bk $BK_USER"
exit 0
.DE
.SH "EXAMPLE 2"
.DS
#!/bin/sh

# Display info about incoming and outgoing csets.

if [ X$BK_STATUS = XDRYRUN -o X$BK_STATUS = XNOTHING ]
then	exit 0
fi
if [ $BK_SIDE = server ]
then U=$BKD_USER
     H=$BKD_HOST
     R=$BKD_ROOT
else U=$BK_USER
     H=$BK_HOST
     R=$BK_ROOT
fi
(
if [ X$BKD_ROOT != X ]
then	printf '%-10s%-20s%-20s\\n' VAR CLIENT SERVER
	printf '%-10s%-20s%-20s\\n' === ====== ======
	printf '%-10s%-20s%-20s\\n' USER $BK_USER $BKD_USER
	printf '%-10s%-20s%-20s\\n' HOST $BK_HOST $BKD_HOST
	printf '%-10s%-20s%-20s\\n' ROOT $BK_ROOT $BKD_ROOT
	printf '%-10s%-20s%-20s\\n' LEVEL $BK_LEVEL $BKD_LEVEL
	printf '%-10s%-20s%-20s\\n' TIME_T $BK_TIME_T $BKD_TIME_T
	printf '%-10s%-20s%-20s\\n' UTC $BK_UTC $BKD_UTC
	printf '%-10s%-20s%-20s\\n' VERSION $BK_VERSION $BKD_VERSION
	echo
fi
echo ${U}@${H} fired the $BK_TRIGGER trigger in $R
case $BK_TRIGGER in
    pre-outgoing)	VERB=Sending;;
    post-outgoing)	VERB=Sent;;
    pre-incoming)	VERB=Receiving;;
    post-incoming)	VERB=Received;;
    pre-resolve)	VERB=Resolving;;
    pre-commit)		VERB=Committing;;
    post-commit)	VERB=Committed;;
    pre-apply)		VERB=Applying;;
esac
if [ X$BK_PENDING != X ]
then	(
	echo $VERB the following deltas
	echo
	bk log - < $BK_PENDING
	) | sed 's/^/    /'
fi
if [ X$BK_CSETLIST != X ]
then	(
	echo $VERB the following changesets
	echo
	bk changes -v - < $BK_CSETLIST
	) | sed 's/^/    /'
fi
if [ X$BK_CSETS != X ]
then	(
	echo $VERB the following changesets
	echo
	bk changes -v -r$BK_CSETS
	) | sed 's/^/    /'
fi
) | mail -s "$BK_EVENT in ${H}:${R}" notify@bitkeeper.com
.DE
.SH "EXAMPLE 3"
.DS
#!/bin/sh
#
# Using pre-commit trigger to verify changeset comment
# BitKeeper/trigger/pre-commit.cset_comments
#

# only run in the product (or a traditional repo)
bk repotype -q
test $? -eq 1 && exit 0

# if this is a merge, we are in the RESYNC directory
# since it is not a user cset, ignore
test "`basename "$BK_ROOT"`" = "RESYNC" && exit 0

grep -q 'BUGID:' $BK_COMMENTFILE && exit 0

msg="A 'BUGID:' field is needed in the checkin comments"

# Bring up an external program to browse bugs so that the
# engineer can add a valid bugid to the changeset comments.

#/opt/bugtrack/bin/bugviewer 'http://server.host.com/bugs?status=open' &


# Ask user if he needs to enter a bugid.  returning an exit code of 2
# from the trigger will allow user to retry the commit from
# within citool.
bk prompt -y"Reenter bugid" -n"Force commit with no bugid" "$msg" && exit 2

# If you wish to provide the option to fail out of citool, use this.
#bk prompt -y"Abort commit" -n"Force commit with no bugid" "$msg" && exit 1

exit 0
.DE
.SH "EXAMPLE 4"
.DS
#!/bin/sh
#
# Using a pre-collapse trigger to checks to see if the key is in
# some repo that is frozen.

bk changes -R bk://work/bk-4.0.x - < $BK_CSETS > /tmp/csets$$
OK=0
test -s /tmp/csets$$ && OK=1
rm -f /tmp/csets$$
exit $OK
.DE
.SH "EXAMPLE 5"
.DS
#!/bin/sh
#
# A post-incoming trigger that will push incoming changesets
# to a mirror repository

MIRROR=REPO-mirror

# Debug
#Q=
#OUT=/tmp/OUT

# Quiet
Q=-q
OUT=/dev/null

# Do not run in components
test `bk repotype` = component && exit 0

# Only run on successful pulls/pushes
test "$BK_EVENT" = "incoming clone" && exit 0
test "$BK_STATUS" != "OK" && exit 0

# Foreground mirror - incoming operation will block
# until the mirror push is complete
#bk push $Q "$MIRROR"
#exit 0

# Background mirror
# Start a background process to push the incoming
# changesets to a mirror repository
#
(
  # Avoid possible race, wait for any locks to be dropped
  bk lock -U
  bk push $Q "$MIRROR"
) > $OUT 2>&1 &
.DE
.SH SEE ALSO
.SA clone
.SA ci
.SA changes
.SA commit
.SA collapse
.SA prompt
.SA pull
.SA push
.SA resolve
.SA repotype
.SA tag
.\" help://trigger
.SH CATEGORY
.B Repository
